package com.zhao.dota.service.impl;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.lang.UUID;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zhao.dota.bo.AuthenticateUser;
import com.zhao.dota.contant.CacheConstant;
import com.zhao.dota.exception.FlowException;
import com.zhao.dota.mapper.UserMapper;
import com.zhao.dota.model.User;
import com.zhao.dota.security.SecurityUtil;
import com.zhao.dota.security.store.TokenStore;
import com.zhao.dota.security.token.AccessToken;
import com.zhao.dota.service.LeagueService;
import com.zhao.dota.service.RoleService;
import com.zhao.dota.service.UserService;
import com.zhao.dota.util.SteamUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;

@Service
@CacheConfig(cacheNames = CacheConstant.USER)
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Resource
    private PasswordEncoder bCryptPasswordEncoder;
    @Resource
    private RoleService roleService;
    @Resource
    private TokenStore tokenStore;
    @Resource
    private LeagueService leagueService;

    private final Cache cache;

    public UserServiceImpl(CacheManager cacheManager) {
        this.cache = cacheManager.getCache(CacheConstant.USER);
    }

    @Override
    @Transactional
    public void signUp(User user) {
        user.setEnabled(true);
        if (StringUtils.hasText(user.getPassword())) {
            user.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));
        }
        user.setCanChangeUsername(true);
        user.setCreateTime(LocalDateTime.now());
        user.setSteamAuthorized(false);
        save(user);
        roleService.addPlayerRole(user);
        leagueService.addPlayer(user);
    }

    @Override
    public String steamLoginCode(AuthenticateUser authenticateUser) {
        String code = UUID.randomUUID().toString(true);
        cache.put("steamLogin:" + code, authenticateUser);
        return code;
    }


    @Override
    public boolean bindSteam(String code, Long steamId) {
        AuthenticateUser authenticateUser = cache.get("steamLogin:" + code, AuthenticateUser.class);
        if (authenticateUser == null) {
            return false;
        }
        User user = getById(authenticateUser.getId());
        user.setSteamId(steamId);
        user.setShortSteamId(SteamUtil.getShortSteamId(steamId));
        user.setSteamAuthorized(true);
        saveOrUpdate(user);
        refreshAuthenticateUser(user, authenticateUser);
        return true;
    }

    @Override
    public User getByOpenId(String openId) {
        LambdaQueryWrapper<User> query = Wrappers.lambdaQuery(User.class)
                .eq(User::getOpenId, openId);
        return getOne(query);
    }

    @Override
    public AccessToken saveWechatUserInfo(String nickName, String avatarUrl, String phone, int gender) {
        AuthenticateUser authenticateUser = SecurityUtil.getAuthenticateUser();
        User user = getById(authenticateUser.getId());
        if (!StringUtils.hasText(user.getNickName())) {
            user.setNickName(nickName);
        }
        if (!StringUtils.hasText(user.getAvatar())) {
            user.setAvatar(avatarUrl);
        }
        if (user.getGender() == null || (user.getGender() == 0 && gender != 0)) {
            user.setGender(gender);
        }
        if (!StringUtils.hasText(user.getPhone())) {
            user.setPhone(phone);
        }
        save(user);
        return refreshAuthenticateUser(user, authenticateUser);
    }

    @Override
    public AccessToken saveUserInfo(String nickName, String avatarUrl, String phone, int gender) {
        AuthenticateUser authenticateUser = SecurityUtil.getAuthenticateUser();
        User user = getById(authenticateUser.getId());
        user.setNickName(nickName);
        user.setAvatar(avatarUrl);
        user.setGender(gender);
        user.setPhone(phone);
        updateById(user);
        return refreshAuthenticateUser(user, authenticateUser);
    }

    @Override
    public boolean resetPassword(Integer id) {
        User user = getById(id);
        if (user == null) {
            return false;
        }
        user.setPassword(bCryptPasswordEncoder.encode("123456"));
        updateById(user);
        return true;
    }

    @Override
    public List<User> findByRole(String roleCode) {
        return baseMapper.findByRoleCode(roleCode);
    }

    @Override
    public void changeUsername(String username) {
        if (!StringUtils.hasText(username) || username.length() < 4) {
            throw new FlowException("用户名太短");
        }
        AuthenticateUser authenticateUser = SecurityUtil.getAuthenticateUser();
        User user = getById(authenticateUser.getId());
        if (Objects.equals(user.getUsername(), username)) {
            throw new FlowException("与原用户名相同，不需要修改");
        }
        LambdaQueryWrapper<User> query = Wrappers.lambdaQuery(User.class)
                .eq(User::getUsername, username);
        List<User> list = list(query);
        if (!list.isEmpty()) {
            throw new FlowException("用户名已存在");
        }
        user.setUsername(username);
        user.setCanChangeUsername(false);
        updateById(user);
        refreshAuthenticateUser(user, authenticateUser);
    }

    @Override
    public boolean changePassword(String original, String changed) {
        AuthenticateUser authenticateUser = SecurityUtil.getAuthenticateUser();
        User user = getById(authenticateUser.getId());
        String s = Base64.decodeStr(original);
        boolean matches = bCryptPasswordEncoder.matches(s, user.getPassword());
        if (!matches) {
            return false;
        }
        String encode = bCryptPasswordEncoder.encode(Base64.decodeStr(changed));
        user.setPassword(encode);
        updateById(user);
        return true;
    }

    private AccessToken refreshAuthenticateUser(User user, AuthenticateUser authenticateUser) {
        AccessToken accessToken = tokenStore.getAccessToken(authenticateUser);
        BeanUtils.copyProperties(user, authenticateUser);
        authenticateUser.setPassword(null);
        tokenStore.storeAccessToken(accessToken, authenticateUser);
        return accessToken;
    }

}
